/**
 * Copyright(c) 2013 Jade Techonologies Co., Ltd.
 *
 * History:
 *   13-4-18 上午10:56 Created by sky
 */
package com.jade.bss.admin.account.impl;

import java.util.Date;
import java.util.List;
import javax.ejb.TransactionAttribute;
import javax.inject.Inject;
import javax.inject.Named;

import com.jade.bss.admin.AdminConstants;
import com.jade.bss.admin.AdminException;
import com.jade.bss.admin.account.AdminAccount;
import com.jade.bss.admin.account.AdminAccountDao;
import com.jade.bss.admin.account.AdminAccountManager;
import com.jade.bss.admin.grant.GrantManager;
import com.jade.bss.admin.principal.SimplePrincipal;
import com.jade.bss.admin.role.RoleManager;
import com.jade.framework.base.criterion.Criteria;
import com.jade.framework.base.criterion.Restrictions;
import com.jade.framework.base.util.PagingList;
import com.jade.framework.base.util.encryption.Encryption;
import com.jade.framework.base.util.encryption.EncryptionUtils;
import com.jade.framework.base.vfile.VFileConstants;
import com.jade.framework.base.vfile.VFileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Transactional;

/**
 * 管理员管理接口实现，采用adminDao方式进行数据访问。
 *
 * @author sky
 * @version 1.0 13-4-18 上午10:56
 */
@Named ("bss_adminAccountManager")
public class AccountManagerImpl
        implements AdminAccountManager
{
    protected AdminAccountDao adminDao;
    protected RoleManager roleManager;
    protected GrantManager grantManager;
    //相同的owner的管理员其账号是否保持唯一。
    protected boolean nameUnique = true;
    protected Encryption encryption = EncryptionUtils.getEncryption("sha1base64");

    @Inject
    public void setRoleManager(@Named ("bss_roleManager") RoleManager roleManager)
    {
        this.roleManager = roleManager;
    }

    @Inject
    public void setAdminAccountDao(@Named ("bss_adminAccountDao") AdminAccountDao adminDao)
    {
        this.adminDao = adminDao;
    }

    @Inject
    public void setGrantManager(@Named ("bss_grantManager") GrantManager grantManager)
    {
        this.grantManager = grantManager;
    }

    public void setNameUnique(boolean nameUnique)
    {
        this.nameUnique = nameUnique;
    }

    //加密
    protected String encryptPassword(String password)
    {
        if (password != null) {
            return encryption.encrypt(password);
        }
        else {
            return null;
        }
    }

    protected void encryptPassword(AdminAccount admin)
    {
        if (encryption != null && StringUtils.isNotEmpty(admin.getPassword())) {
            String encPassword = encryptPassword(admin.getPassword());
            admin.setPassword(encPassword);
        }
    }

    protected void checkName(String type, String ownerType, String ownerId, String name)
            throws AdminException
    {
        AdminAccount admin;
        if (nameUnique) {
            admin = getAdmin(type, name);
        }
        else {
            admin = getAdmin(type, ownerType, ownerId, name);
        }
        if (admin != null) {
            //说明有同名，抛出异常
            throw new AdminException(AdminConstants.ERR_ACCOUNT_DUPLICATED_NAME, "duplicated name", null, name,
                    ownerType, ownerId);
        }
    }

    protected void checkPassword(String encrptPass, String password)
            throws AdminException
    {
        String newEncPassword = encryptPassword(password);
        if (!encrptPass.equals(newEncPassword)) {
            throw new AdminException(AdminConstants.ERR_ACCOUNT_PASSWORD_MISMATCHED, "Password mismatched");
        }
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public int addAdmin(AdminAccount admin, int[] roleIds)
            throws AdminException
    {
        admin.setName(admin.getName().toLowerCase());
        //验证admin账号的唯一性
        checkName(admin.getType(), admin.getOwnerType(), admin.getOwnerId(), admin.getName());
        admin.setCreationDate(new Date());
        encryptPassword(admin); //密码加密
        int adminId = adminDao.addAdmin(admin);
        if (roleIds != null) {
            SimplePrincipal principal = new SimplePrincipal();
            principal.setName(String.valueOf(adminId));
            principal.setType(SimplePrincipal.TYPE_ADMIN);
            grantManager.grantRoles(principal, roleIds);
        }
        return adminId;
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public int addAdmin(AdminAccount admin)
            throws AdminException
    {
        admin.setName(admin.getName().toLowerCase());
        //验证admin账号的唯一性
        checkName(admin.getType(), admin.getOwnerType(), admin.getOwnerId(), admin.getName());
        admin.setCreationDate(new Date());
        encryptPassword(admin); //密码加密
        return adminDao.addAdmin(admin);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void removeAdmin(int... ids)
            throws AdminException
    {
        for (int id : ids) {
            AdminAccount admin = getAdmin(id);
            if (admin != null) {
                adminDao.removeAdmin(admin);
                if (StringUtils.isNotBlank(admin.getLogo())) {
                    VFileUtils.removeFileQuitely(VFileConstants.DATA_TYPE_IMAGE, admin.getLogo());
                }
            }
        }
    }

    public AdminAccount getAdmin(int id)
    {
        return adminDao.getAdmin(id);
    }

    public AdminAccount getAdmin(String type, String name)
    {
        if (nameUnique) {
            Criteria criteria = new Criteria(-1, -1);
            criteria.add(Restrictions.eq("type", type));
            criteria.add(Restrictions.eq("name", name));
            List<AdminAccount> accounts = adminDao.listAdmin(criteria);
            return accounts.isEmpty() ? null : accounts.get(0);
        }
        else {
            return getAdmin(type, AdminAccount.DEFAULT_OWNER_TYPE, AdminAccount.DEFAULT_OWNER_ID, name);
        }
    }

    public AdminAccount getAdmin(String type, String ownerType, String ownerId, String name)
    {
        Criteria criteria = new Criteria(-1, -1);
        criteria.add(Restrictions.eq("type", type));
        criteria.add(Restrictions.eq("ownerType", ownerType));
        criteria.add(Restrictions.eq("ownerId", ownerId));
        criteria.add(Restrictions.eq("name", name));
        List<AdminAccount> accounts = adminDao.listAdmin(criteria);
        if (accounts.isEmpty()) {
            return null;
        }
        else {
            return accounts.get(0);
        }
    }

    protected AdminAccount loadAdmin(String type, String name)
            throws AdminException
    {
        AdminAccount admin = getAdmin(type, name);
        if (admin == null) {
            throw new AdminException(AdminConstants.ERR_ACCOUNT_NOT_EXISTED, "account not existed", null, type, name);
        }
        return admin;
    }

    protected AdminAccount loadAdmin(String type, String ownerType, String ownerId, String name)
            throws AdminException
    {

        AdminAccount admin = getAdmin(type, ownerType, ownerId, name);
        if (admin == null) {
            throw new AdminException(AdminConstants.ERR_ACCOUNT_NOT_EXISTED, "account not existed", null, type, name,
                    ownerType, ownerId);
        }
        return admin;
    }

    protected AdminAccount loadAdmin(int id)
            throws AdminException
    {
        AdminAccount admin = adminDao.getAdmin(id);
        if (admin == null) {
            throw new AdminException(AdminConstants.ERR_ACCOUNT_NOT_EXISTED, "account not existed", null, id);
        }
        return admin;
    }

    public AdminAccount validateAdmin(String name, String password)
            throws AdminException
    {
        return validateAdmin(AdminAccount.DEFAULT_TYPE, name, password);
    }

    public AdminAccount validateAdmin(String type, String name, String password)
            throws AdminException
    {
        if (nameUnique) {
            AdminAccount admin = loadAdmin(type, name);
            checkPassword(admin.getPassword(), password);
            return admin;
        }
        else {
            return validateAdmin(type, AdminAccount.DEFAULT_OWNER_TYPE, AdminAccount.DEFAULT_OWNER_ID, name, password);
        }
    }

    public AdminAccount validateAdmin(String type, String ownerType, String ownerId, String name, String password)
            throws AdminException
    {
        AdminAccount admin = loadAdmin(type, ownerType, ownerId, name);
        checkPassword(admin.getPassword(), password);
        return admin;
    }

    public PagingList<AdminAccount> queryAdmin(Criteria criteria, int offset, int length)
    {
        criteria.setOffset(offset);
        criteria.setLength(length);
        return queryAdmin(criteria);
    }

    public PagingList<AdminAccount> queryAdmin(Criteria criteria)
    {
        return adminDao.queryAdmin(criteria);
    }

    public PagingList<AdminAccount> queryAdmin(String type, String ownerType, String ownerId, int offset, int length)
    {
        Criteria criteria = new Criteria(offset, length);
        criteria.add(Restrictions.eq("type", type));
        criteria.add(Restrictions.eq("ownerType", ownerType));
        criteria.add(Restrictions.eq("ownerId", ownerId));
        return queryAdmin(criteria);
    }

    public List<AdminAccount> listAdmin(Criteria criteria)
    {
        return adminDao.listAdmin(criteria);
    }

    public List<AdminAccount> listAdmin(String type, String ownerType, String ownerId)
    {
        Criteria criteria = new Criteria();
        criteria.add(Restrictions.eq("type", type));
        criteria.add(Restrictions.eq("ownerType", ownerType));
        criteria.add(Restrictions.eq("ownerId", ownerId));
        return listAdmin(criteria);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void updateAdmin(AdminAccount admin)
            throws AdminException
    {
        admin.setName(admin.getName().toLowerCase());
        AdminAccount old;
        if (nameUnique) {
            old = getAdmin(admin.getType(), admin.getName());
        }
        else {
            old = getAdmin(admin.getType(), admin.getOwnerType(), admin.getOwnerId(), admin.getName());
        }
        if (old != null && old.getId() != admin.getId()) {
            throw new AdminException(AdminConstants.ERR_ACCOUNT_DUPLICATED_NAME, "duplicated name", null,
                    admin.getName(), admin.getOwnerType(), admin.getOwnerId());
        }
        old = getAdmin(admin.getId());
        old.setRealName(admin.getRealName());
        old.setName(admin.getName());
        old.setBirthday(admin.getBirthday());
        old.setRegion(admin.getRegion());
        old.setEmail(admin.getEmail());
        old.setStatus(admin.getStatus());
        old.setPhone(admin.getPhone());
        old.setMobile(admin.getMobile());
        old.setGender(admin.getGender());
        boolean changeLogo = !StringUtils.equalsIgnoreCase(old.getLogo(), admin.getLogo());
        String logo = old.getLogo();
        old.setLogo(admin.getLogo());
        adminDao.updateAdmin(old);
        if (changeLogo && StringUtils.isNotBlank(logo)) {
            VFileUtils.removeFileQuitely(VFileConstants.DATA_TYPE_IMAGE, logo);
        }
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void modifyPassword(int id, String oldPassword, String newPassword)
            throws AdminException
    {
        AdminAccount admin = loadAdmin(id);
        checkPassword(admin.getPassword(), oldPassword);
        admin.setPassword(encryptPassword(newPassword));
        adminDao.updateAdmin(admin);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void resetPassword(int id, String newPassword)
            throws AdminException
    {
        AdminAccount admin = loadAdmin(id);
        admin.setPassword(encryptPassword(newPassword));
        adminDao.updateAdmin(admin);//重置密码
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void updateStatus(int status, int... ids)
            throws AdminException
    {
        for (int id : ids) {
            AdminAccount admin = loadAdmin(id);
            admin.setStatus(status);
            adminDao.updateAdmin(admin);
        }
    }

}
